// Copyright 2011 Google Inc. All Rights Reserved.

package naclrun

import (
	"fmt"
	"os"
	"path"
	"strings"

//	"path_proxy"
//	"proto"
)


// A Resolver translates a virtual file name into an actual file name.
type Resolver interface {
	ResolveFile(filename string) (realFileName string, ok bool)
}

/*
// ProtoResolver implements Resolver on top of protobuf config.
type ProtoResolver struct {
	pathRules map[string]*path_proxy.PathRule
	fileRules map[string]*path_proxy.FileRule
}

// NewProtoResolver returns a new ProtoResolver
 with the given path and file rules.
func NewProtoResolver(pathRules []*path_proxy.PathRule, fileRules []*path_proxy.FileRule) (res *ProtoResolver) {
	pathRuleMap := make(map[string]*path_proxy.PathRule)
	for _, rule := range pathRules {
		pathRuleMap[*rule.Path] = rule
	}
	fileRuleMap := make(map[string]*path_proxy.FileRule)
	for _, rule := range fileRules {
		fileRuleMap[*rule.Path] = rule
	}
	return &ProtoResolver{
		pathRules: pathRuleMap,
		fileRules: fileRuleMap,
	}
}

// FromTextProto returns a ProtoResolver with the rules parsed from
// the given filemap using the text codec for protocol buffers.
func FromTextProto(filemap string) (r *ProtoResolver, err os.Error) {
	pathRules, fileRules, err := parseRules(filemap)
	if err != nil {
		return
	}
	return NewProtoResolver(pathRules, fileRules), nil
}

func parseRules(filemap string) (pathRules []*path_proxy.PathRule, fileRules []*path_proxy.FileRule, err os.Error) {
	if filemap == "" {
		return
	}
	list := new(path_proxy.RuleList)
	err = proto.UnmarshalText(filemap, list)
	if err != nil {
		return
	}
	for _, r := range list.Path {
		if r.Path == nil || r.RealPath == nil {
			continue
		}
		pathRules = append(pathRules, r)
	}
	for _, r := range list.File {
		if r.Path == nil || r.RealPath == nil {
			continue
		}
		fileRules = append(fileRules, r)
	}

	return
}

func (r *ProtoResolver) findPathRule(filename string) (rule *path_proxy.PathRule, ok bool) {
	cur := filename
	// Search for path prefix
	for {
		cur, _ = path.Split(cur)
		if cur == "" {
			return
		}
		rule, ok = r.pathRules[cur]
		if ok {
			return
		}
		if cur == "/" {
			return
		}
		cur = cur[:len(cur)-1]
	}
	return
}

// AddPathRule adds the new path rule to ProtoResolver.
func (r *ProtoResolver) AddPathRule(path, realPath string) {
	r.pathRules[path] = &path_proxy.PathRule{
		Path:     proto.String(path),
		RealPath: proto.String(realPath),
	}
}

// ResolveFile implements Resolver.ResolveFile.
func (r *ProtoResolver) ResolveFile(filename string) (realFileName string, ok bool) {
	filename = path.Clean(filename)
	if filename[len(filename)-1] == '/' {
		// Resolve files only.
		return
	}
	fileRule, ok := r.fileRules[filename]
	if ok && *fileRule.Path == filename {
		return *fileRule.RealPath, true
	}
	pathRule, ok := r.findPathRule(filename)
	if !ok || !strings.HasPrefix(filename, *pathRule.Path) {
		return
	}
	suffix := filename[len(*pathRule.Path):]
	if path.IsAbs(suffix) {
		suffix = suffix[1:]
	}
	return path.Join(*pathRule.RealPath, suffix), true
}
*/

// CompositeResolver implements Resolver that
// tries a sequence of resolvers one by one.
type CompositeResolver []Resolver

// ResolveFile implements Resolver.ResolveFile.
func (r *CompositeResolver) ResolveFile(filename string) (realFileName string, ok bool) {
	for _, res := range *r {
		realFileName, ok = res.ResolveFile(filename)
		if ok {
			return
		}
	}
	return
}

// Add adds a resolver to the list.
func (r *CompositeResolver) Add(res Resolver) {
	*r = append(*r, res)
}

// NewCompositeResolver returns a new instance of CompositeResolver
// using the given resolvers.
func NewCompositeResolver(res ...Resolver) *CompositeResolver {
	r := CompositeResolver(res)
	return &r
}

// A VirtualRootResolver resolves names within a virtual chroot directory.
type VirtualRootResolver struct {
	workDirRoot string
	// Relative current work dir
	cwd string
}

// NewVirtualRootResolver returns a new instance of VirtualRootResolver with the given directory as a root.
func NewVirtualRootResolver(workDirRoot string) *VirtualRootResolver {
	return &VirtualRootResolver{workDirRoot: workDirRoot}
}

// ResolveFile implements Resolver.ResolveFile.
func (r *VirtualRootResolver) ResolveFile(filename string) (realFileName string, ok bool) {
	filename = path.Clean(filename)
	if path.IsAbs(filename) {
		filename = filename[1:]
	} else {
		filename = path.Clean(path.Join(r.cwd, filename))
	}

	if filename == ".." || strings.HasPrefix(filename, "../") {
		return
	}
	return path.Join(r.workDirRoot, filename), true
}

// SetCWD sets current working directory to the specified path.
func (r *VirtualRootResolver) SetCWD(cwd string) os.Error {
	cwd = path.Clean(cwd)
	if path.IsAbs(cwd) {
		cwd = cwd[1:]
	}
	if cwd == ".." || strings.HasPrefix(cwd, "../") {
		return fmt.Errorf("Can't set cwd upper than virtual root. Requested: %s", cwd)
	}
	r.cwd = cwd
	return nil
}
